/*
size em_conf_t 608
size int 4
typedef struct {
	uint16_t device_id; //2 byte +6byte padding
    int event_timer;  //4 byte
	int thread_per_core; //4 byte
	int process_per_core;//4 byte
	int core_count;//4 byte   
	em_core_mask_t phys_mask; //128 byte  128+24=152
	em_pool_cfg_t default_pool_cfg;//120 byte 152+120=272
	struct {
		em_log_func_t log_fn;
		em_vlog_func_t vlog_fn;
	} log; //16 byte  288
	
	struct {
		em_input_poll_func_t input_poll_fn;
		em_core_mask_t input_poll_mask;
	} input;//136 byte   424
	
	struct {
		em_output_drain_func_t output_drain_fn;
		em_core_mask_t output_drain_mask;
	} output;//136 byte  560

	em_api_hooks_t api_hooks; //24 byte
	em_idle_hooks_t idle_hooks;//24 byte
} em_conf_t;

void em_conf_init(em_conf_t *conf)
{
	if (unlikely(!conf)) {
		INTERNAL_ERROR(EM_FATAL(EM_ERR_BAD_POINTER),
			       EM_ESCOPE_CONF_INIT, "Conf pointer NULL!");
		return;
	}
	memset(conf, 0, sizeof(em_conf_t));
	em_pool_cfg_init(&conf->default_pool_cfg);
}

typedef struct odp_init_t {
	int num_worker;
	int num_control;
	const odp_cpumask_t *worker_cpus;
	const odp_cpumask_t *control_cpus;
	odp_log_func_t log_fn;
	odp_abort_func_t abort_fn;
	odp_feature_t not_used;
	odp_mem_model_t mem_model;

	struct {
		uint64_t max_memory;
	} shm;
} odp_init_t;
*/
#![feature(c_variadic)]
use std::ptr;
extern crate libc;

#[repr(C)]
pub struct OdpInitT {
    num_worker: i32,
    num_control: i32,
    worker_cpus: *const OdpCpuMask,
    control_cpus: *const OdpCpuMask,
    log_fn: Option<LogFn>,
    abort_fn: AbrtFn,
    //not_used: odp_feature_t,
    //mem_model: odp_mem_model_t,
    shm: OdpShmT,
}

impl Default for OdpInitT {
    fn default() -> Self {
        OdpInitT {
            num_worker: 0,
            num_control: 0,
            worker_cpus: ptr::null(),
            control_cpus: ptr::null(),
            log_fn: None,
            abort_fn: odp_abort,
            //not_used: Default::default(),
            //mem_model: Default::default(),
            shm: Default::default(),
        }
    }
}

type AbrtFn = fn();

use std::process;
fn odp_abort() {
    process::abort();
}

#[repr(C)]
pub struct OdpShmT {
    max_memory: u64,
}

impl Default for OdpShmT {
    fn default() -> Self {
        OdpShmT {
            max_memory: 0,
        }
    }
} 


type OdpInstant = u64;


#[repr(C)]
//#[repr(align(64))] 
pub struct EmConfT {
    pub device_id: u16,
    pub padding: [u8; 6],  //8 -> 8    
    pub event_timer: i32,
    pub thread_per_core: i32,//8->16
    pub process_per_core: i32,
    pub core_count: i32,      //8 -> 24
    pub phys_mask: EmCoreMask, //128 -> 152
    pub default_pool_cfg: EmPoolCfg,//120 -> 272
    pub log: EmLogConfT,  //16 -> 288
    pub input: EmInput, //136 -> 424
    pub output: EmOutput,//136 -> 560
    pub api_hooks: EmApiHooksT, //24 -> 584
    pub idle_hooks: EmIdleHooksT, //24 -> 608
}

/*typedef struct {
	odp_cpumask_t odp_cpumask;
} em_core_mask_t;*/

//const ODP_CPUMASK_SIZE: usize = std::mem::size_of::<OdpCpuMask>() * 8;
//const ODP_CPUMASK_STR_SIZE: usize = (ODP_CPUMASK_SIZE + 3) / 4 + 3;

#[repr(C, align(8))]
pub struct OdpCpuMask {
    _u8: [u8; 128],
}//128 byte


#[repr(C)]
pub struct EmCoreMask {
    pub odp_cpumask: OdpCpuMask,
} 

impl Default for EmCoreMask {
    fn default() -> Self {
        let mut odp_cpumask = OdpCpuMask {
            _u8: [0; 128],
        };

        odp_cpumask._u8[0] = 0x1;
        EmCoreMask { odp_cpumask }
    }
}



impl EmCoreMask {
    pub fn new() -> Self {
        EmCoreMask {
            odp_cpumask: OdpCpuMask {
                _u8: [0; 128],
            },
        }
    }
}

/////////////////////////////////////////
/// EmPoolCfg  120byte
/////////////////////////////////////////
#[repr(C)]
pub struct EmEventType {
    pub event_type: i32, // em_event_type_t
}

#[repr(C)]
pub struct AlignOffset {
    pub in_use: bool,
    pub value: u32,
}

#[repr(C)]
pub struct UserArea {
    pub in_use: bool,
    pub size: usize,
}

#[repr(C)]
pub struct PktHeadroom {
    pub in_use: bool,
    pub value: u32,
}

#[repr(C)]
pub struct Pkt {
    pub headroom: PktHeadroom,
}

#[derive(Clone, Copy)]
#[repr(C)]
pub struct Subpool {
    pub size: u32,
    pub num: u32,
    pub cache_size: u32,
}

impl Default for Subpool {
    fn default() -> Self {
        Subpool {
            size: 0,
            num: 0,
            cache_size: 0,
        }
    }
}

#[repr(C)]
pub struct EmPoolStatsOpt {
    pub in_use: bool,
    pub opt: i32, // em_pool_stats_opt_t
}

#[repr(C)]
pub struct EmPoolCfg {
    pub event_type: EmEventType,
    pub align_offset: AlignOffset,
    pub user_area: UserArea,
    pub pkt: Pkt,
    pub num_subpools: i32,
    pub subpool: [Subpool; 5], // EM_MAX_SUBPOOLS
    pub stats_opt: EmPoolStatsOpt,
    pub __internal_check: u32,
}

impl Default for EmPoolCfg {
    fn default() -> Self {
        EmPoolCfg {
            event_type: EmEventType {
                event_type: 0,
            },
            align_offset: AlignOffset {
                in_use: false,
                value: 0,
            },
            user_area: UserArea {
                in_use: false,
                size: 0,
            },
            pkt: Pkt {
                headroom: PktHeadroom {
                    in_use: false,
                    value: 0,
                },
            },
            num_subpools: 0,
            subpool: [Subpool::default(); 5],
            stats_opt: EmPoolStatsOpt {
                in_use: false,
                opt: 0,
            },
            __internal_check: 0,
        }
    }
}

/////////////////////////////////////////
/// em_log_config_t
/// typedef int (*em_log_func_t)(EmLogLevelT level, const char *fmt, ...)
///   __attribute__((format(printf, 2, 3)));
/////////////////////////////////////////
#[repr(C)]
#[derive(Debug)]
pub enum EmLogLevelT {
    EmLogDebug,
    EmLogInfo,
    EmLogWarn,
    EmLogError,
}

pub enum EmThreadTypeT {
    WORKER,
    CONTROL,
}

impl Default for EmLogLevelT {
    fn default() -> Self {
        EmLogLevelT::EmLogDebug
    }
}

type LogFn = fn(EmLogLevelT, &str, args: std::ffi::VaList) -> i32;
type VLogFn = fn(EmLogLevelT, &str, *mut std::ffi::c_void) -> i32;

#[repr(C)]
pub struct EmLogConfT {
    pub log_fn: Option<LogFn>,
    pub vlog_fn: Option<VLogFn>,
}

impl Default for EmLogConfT {
    fn default() -> Self {
        EmLogConfT {
            log_fn: None,
            vlog_fn: None,
        }
    }
}

pub struct EmInput {
    pub input_poll_fn: EmInputPollFuncT,
    pub input_poll_mask: EmCoreMask,
}

pub type EmInputPollFuncT = fn ();

fn default_em_input_poll() {
    println!("default_em_input_poll");
}

impl Default for EmInput {
    fn default() -> Self {
        EmInput {
            input_poll_fn: default_em_input_poll,
            input_poll_mask: EmCoreMask::default(),
        }
    }
}
pub struct EmOutput {
    pub output_poll_fn: EmOutputPollFuncT,
    pub output_poll_mask: EmCoreMask,
}
pub type EmOutputPollFuncT = fn();

fn default_em_output_poll() {
    println!("default_em_output_poll");
}
impl Default for EmOutput {
    fn default() -> Self {
        EmOutput {
            output_poll_fn: default_em_output_poll,
            output_poll_mask: EmCoreMask::default(),
        }
    }
}

pub type EmQueueT = i32;
pub type EmEventT = i32;
pub type EmEventTypeT = i32;
pub type EmPoolT = i32;
pub type EmEventGroupT = i32;


pub type EmApiHookAllocT = fn(events: &[EmEventT], num_act: i32, num_req: i32, size: u32, event_type: EmEventTypeT, pool: EmPoolT);
pub type EmApiHookFreeT = fn(events: &[EmEventT], num: i32);
pub type EmApiHookSendT = fn(events: &[EmEventT], num: i32, queue: EmQueueT, event_group: EmEventGroupT);

pub fn default_em_api_hook_alloc (events: &[EmEventT], num_act: i32, num_req: i32, size: u32, event_type: EmEventTypeT, pool: EmPoolT) {
    println!("default_em_api_hook_alloc: events: {:?} num_act: {} num_req: {} size: {} event_type: {} pool: {}", events, num_act, num_req, size, event_type, pool);
}

pub fn default_em_api_hook_free (events: &[EmEventT], num: i32) {
    println!("default_em_api_hook_free: events: {:?} num: {}", events, num);
}

pub fn default_em_api_hook_send (events: &[EmEventT], num: i32, queue: EmQueueT, event_group: EmEventGroupT) {
    println!("default_em_api_hook_send: events: {:?} num: {} queue: {} event_group: {}", events, num, queue, event_group);
}

pub struct EmApiHooksT {
    pub alloc_hook: EmApiHookAllocT,
    pub free_hook: EmApiHookFreeT,
    pub send_hook: EmApiHookSendT,
}

impl Default for EmApiHooksT {
    fn default() -> Self {
        EmApiHooksT {
            alloc_hook: default_em_api_hook_alloc,
            free_hook: default_em_api_hook_free,
            send_hook: default_em_api_hook_send,
        }
    }
}

pub type EmIdleHookToIdleT = fn(to_idle_delay_ns: u64);
pub type EmIdleHookToActiveT = fn();
pub type EmIdleHookWhileIdleT = fn();

pub fn default_em_idle_hook_to_idle (to_idle_delay_ns: u64) {
    println!("default_em_idle_hook_to_idle: to_idle_delay_ns: {}", to_idle_delay_ns);
}

pub fn default_em_idle_hook_to_active () {
    println!("default_em_idle_hook_to_active");
}

pub fn default_em_idle_hook_while_idle () {
    println!("default_em_idle_hook_while_idle");
}
pub struct EmIdleHooksT {
    pub to_idle_hook: EmIdleHookToIdleT,
    pub to_active_hook: EmIdleHookToActiveT,
    pub while_idle_hook: EmIdleHookWhileIdleT,
}

impl Default for EmIdleHooksT {
    fn default() -> Self {
        EmIdleHooksT {
            to_idle_hook: default_em_idle_hook_to_idle,
            to_active_hook: default_em_idle_hook_to_active,
            while_idle_hook: default_em_idle_hook_while_idle,
        }
    }
} 


extern  {
    fn odp_init_param_init(init: *mut OdpInitT);
    fn odp_init_global(instance: *mut OdpInstant, init: *const OdpInitT, instance_param: *mut OdpInitT) -> i32;
    fn odp_term_global(instance: OdpInstant) -> i32;
    fn odp_init_local(instance: OdpInstant, thr_type:EmThreadTypeT) -> i32;
    fn odp_term_local() -> i32;
    fn em_init(conf: *const EmConfT) -> i32;
    fn em_init_core() -> i32;
    fn em_term_core() -> i32;
    fn em_term(conf: *const EmConfT) -> i32;
    fn em_pool_cfg_init(pool_cfg: *mut EmPoolCfg);
}

pub fn rust_em_init(conf: &mut EmConfT) -> i32 {
    let mut init_params: OdpInitT = Default::default();
    let mut instance: OdpInstant = Default::default();

	unsafe {
		odp_init_param_init(&mut init_params);
	}
    // 
    // init_params.num_worker = cpu_conf.num_worker;
    // init_params.worker_cpus = &cpu_conf.worker_mask;
    // init_params.not_used.feat.cls = 1;
    // init_params.not_used.feat.compress = 1;
    // init_params.not_used.feat.crypto = 1;
    // init_params.not_used.feat.ipsec = 1;
    // init_params.not_used.feat.tm = 1;


    unsafe {
        let stage = odp_init_global(&mut instance, &init_params, ptr::null_mut());
        println!("odp_init_global stage {}", stage);
        odp_init_local(instance, EmThreadTypeT::CONTROL);
    }
    // Implement the rest of the function as needed

    // Return the ODP instance


    println!("em_pool_cfg_init start");
    unsafe {        
        em_pool_cfg_init(&mut conf.default_pool_cfg);
    }
    
    println!("em_pool_cfg_init end");
    println!("conf.core_count: {}", conf.core_count);

    unsafe {  
        em_init(conf as *const EmConfT);       //create_ctrl_queues  
        println!("em_init ============ end");

        em_init_core();    
        println!("em_init_core ============ end");     
    }
    
    //std::thread::sleep(std::time::Duration::from_secs(60));

    unsafe {
        println!("em_term_core ============ begin"); 
        em_term_core();//  -> delete_ctrl_queues
        em_term(conf as *const EmConfT);
        odp_term_local();
        odp_term_global(instance);
    }

    return 0;
}